#include <iostream>
#include <pthread.h>
#include <queue>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

using namespace std;



#include <signal.h>

#ifdef _POSIX_THREAD_PROCESS_SHARED
  printf("It's implemented: %ld\n", sysconf(_POSIX_THREAD_PROCESS_SHARED));
#endif
//Описание функции потока
void* ThreadRun(void* params);
//Функция которая будет выводить массив в консоль
void ArrayShow(int arr[]);
//Функция будет заполнять массив случайными числами
void ArrayChange(int arr[]);
//Наш объект(массив), к которому должен быть синхронизирован доступ
int myArray[10];
//Критическая секция для массива
pthread_mutex_t mArray; 
 
int main(int argc, char* argv[])
{
    //Заполним массив числами
    for (int i = 0; i < 10; ++i)
        myArray[i] = i;
    //Инициализируем критическую секцию
    pthread_mutexattr_t mAttr;  // можно задавать самыеп разнве атрибуты(но так кучу всего по умолчанию)
    pthread_mutexattr_init(&mAttr);  // инициализация
    pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_RECURSIVE); // тип блокировки, вообще ее используют для trylock чтобы вызовать секцию скольугодно раз одним и тем же потоком, но потом ее нужно закрыть столько же 
    pthread_mutex_init(&mArray, &mAttr);
    pthread_mutexattr_destroy(&mAttr);
   // InitializeCriticalSection(&csArray); 
 
    std::cout << "Array before:\n";
    ArrayShow(myArray);

    //Запустим 5 потоков
    for (int i = 0; i < 5; ++i)
    {
        //Каждому потоку, в качестве параметра передаем его номер
        int* number = new int;
        number[i] = i;
        //_beginthread(ThreadRun, 0, (void*) number);
        pthread_create((pthread_t *) number, NULL, ThreadRun, number);
    }
 
    //Ждем, что бы пользователь сообщил о завершении потоков
    char isFinished = 'n';
    while (isFinished != 'y')
    {
        std::cout << "MAIN_THREAD: All threads is finished? (y/n)\n";
        std::cin >> isFinished;
    }
 
    //Удаляем критическую секцию
    pthread_mutex_destroy(&mArray);
    //DeleteCriticalSection(&csArray);
 
    std::cout << "Array after:\n";
    ArrayShow(myArray);
 
     //Пауза
    cin.ignore().get(); 
    return 0;
}
 
void* ThreadRun(void* params)
{
    //Преобразуем входящие параметры в int*
    int* myNumber = (int*) params;
    std::string start;
    std::string enter;
    std::string leave;
 
    char buff[10];
    sprintf(buff, "%i", *myNumber);
    start.append("Thread["); start.append(buff); start.append("] start...\n");
    enter.append("Thread["); enter.append(buff); enter.append("] enter in critical section...\n");
    leave.append("Thread["); leave.append(buff); leave.append("] leave critical section...\n");
 
    std::cout << start.c_str();
 
    //Входим в критическую секцию
    //EnterCriticalSection(&csArray);
     pthread_mutex_lock(&mArray);
     std::cout << enter.c_str();
    //Задержка для наглядности процесса
    ArrayChange(myArray);
    ArrayShow(myArray);
    //Выходим из критической секции
    //LeaveCriticalSection(&csArray);
    pthread_mutex_unlock(&mArray);
    std::cout << leave.c_str();
}
 
void ArrayShow(int arr[])
{
    std::string line;
    for (int i = 0; i < 10; ++i)
    {
        char buff[10];
	arr[i]-=1;
        sprintf(buff,"%d ",arr[i]);
        line.append(buff);
        line.append("-");
    }
    line.append("\n");
    std::cout << line.c_str();

}
 
void ArrayChange(int arr[])
{

    for (int i = 0; i < 10; ++i)
    {
        myArray[i] = rand() % 100;
    }
}
